﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
using System.Diagnostics;
using System.Windows;
using Microsoft.Data.Caching;
using Microsoft.Data.Caching.AdminApi;

namespace MDCAdminTool
{
    public abstract class PowerShellBase<TParent,TChild> : CacheEntityBase , IDisposable , ICacheCommand<TChild>
        where TChild : CacheEntityBase
    {
        #region Fields
        protected Runspace RunSpace;
        protected Pipeline PipeLine;
        protected Dictionary<string, object> Params;

        public bool IsRunSpaceOpen { get; set; }
        public new TParent Parent { get; set; }
        #endregion

        protected PowerShellBase()
        {
            IsRunSpaceOpen = false;
            OpenRunSpace();
        }
        ~PowerShellBase()
        {
            Dispose();
        }

        private void OpenRunSpace()
        {
            PSSnapInException snapInException = null;

            try
            {
                RunspaceConfiguration rsConfig    = RunspaceConfiguration.Create();
                PSSnapInInfo info = rsConfig.AddPSSnapIn( "Microsoft.Data.Caching.Commands" , out snapInException );

                RunSpace = RunspaceFactory.CreateRunspace( rsConfig );
                RunSpace.Open();

                IsRunSpaceOpen = true;
            }
            catch ( Exception ex)
            {
                IsRunSpaceOpen = false;

                if (snapInException != null) 
                    Log( new ErrorMessage { Message = snapInException.Message } );

                Log( new ErrorMessage { Message = ex.Message } );
            }
        }

        protected Collection<PSObject> Invoke( string commandName )
        {
            return Invoke(commandName, new Dictionary<string, object>());
        }
        protected Collection<PSObject> Invoke( string commandName , Dictionary<string , object> commands )
        {
            Collection<PSObject> Result = null;
            AddResultToLog( new PowerShellCommand{ CommandName = commandName, Parameters = commands} );
            try
            {
                var cmd = new Command( commandName );

                foreach ( var c in commands )
                {
                    cmd.Parameters.Add( new CommandParameter( c.Key , c.Value ) );
                }
                
                if( RunSpace == null )
                {
                    OpenRunSpace();       
                }

                PipeLine = RunSpace.CreatePipeline();
                PipeLine.Commands.Add( cmd );
                Result = PipeLine.Invoke();

                InsertResultToOutput(Result);
            }
            catch ( Exception exception)
            {
                Debug.WriteLine( exception.Message );
                if ( Output != null )
                {
                    AddResultToLog( new ErrorMessage { Message = exception.Message } );
                }
            }

            return Result;
        }
        
        void InsertResultToOutput( IEnumerable<PSObject> result )
        {
            foreach (var obj in result)
            {
                if ( Output != null )
                {
                    if ( obj.BaseObject.GetType().Name == "HostCacheStats" )
                    {
                        AddResultToLog( CacheHost.ConvertPSObjectToHostCacheStats( obj ) );
                    }
                    else if ( obj.BaseObject.GetType().Name == "NamedCacheStats" )
                    {
                        AddResultToLog( MdcCache.ConvertPSObjectToNamedCacheStats( obj ) );
                    }
                    else if ( obj.BaseObject.GetType().Name == "CacheConfig" )
                    {
                        AddResultToLog( MdcCache.ConvertPSObjectToCacheConfig( obj ) );
                    }
                    else if ( obj.BaseObject.GetType().Name == "HostConfig" )
                    {
                        AddResultToLog( CacheHost.ConvertPSObjectToHostConfig( obj ) );
                    }
                    else if ( obj.BaseObject.ToString().Contains("ErrorCode") )
                    {
                        AddResultToLog( new ErrorMessage{ Message =  obj.BaseObject.ToString() } );
                    }
                    else
                    {
                        AddResultToLog( obj.BaseObject );
                    }
                }
            }
        }
        protected void AddResultToLog( object obj )
        {
            if( Output == null ) return;

            Output.Dispatcher.BeginInvoke(
                new Action( () => Log( obj ) ) );
        }

        #region IDisposable Members
        public void Dispose()
        {
            if (RunSpace != null) RunSpace.CloseAsync();
            GC.SuppressFinalize(this);
        }

        #endregion

        #region ICacheCommand<TChild> Members
        public virtual void Add( TChild item )
        {
            Items.Add( item );
        }
        public virtual void Remove( TChild item )
        {
            Items.Remove(item);
        }
        public Collection<TChild> Items { get; set; }
        #endregion
    }
}
